#developer experience

1 post.

Working on Environment Sync v2

The Environment Sync v2 dialog with tables grouped by site, with sizes, and one subsite unticked

Environment Sync is how developers on Altis copy a site’s database and uploads from one environment to another. You might copy production to staging to reproduce a bug, or to work with real content.

Version 2 made that copy faster and less manual. You can choose which tables and subsites to bring over. URLs are replaced during the import. Post-sync steps run on their own. Every import stays in a history you can come back to. The same sync also runs on your laptop with one command.

Mockups and scope

When I joined the project, I started with exploratory work. I wasn’t sure how it all fit together, so I asked. Were we improving the existing sync, or just adding a few specific features that were requested?

The answer was both.

I think best with tangible things. I also kind of missed flexing my design muscles. So I went ahead and created mockups of what could be improved and how I imagined it would look. It also helped the discussion move forward, since the team had something to comment on.

Here are some of the things that shipped.

Table and subsite selection

Large WordPress multisites can have databases of several hundred GB. A developer working on one subsite doesn’t need all of it.

I built the sync dialog so it loads the source tables and groups them by site, with each site’s domain and size. This one stayed close to the mockup. You can untick whole sites or search for a single table. The summary updates as you go, so you know how much you’re about to copy.

On the backend, I made the sync read the search-replace mapping from the project’s composer.json. You don’t have to remember a separate command afterward. Post-sync hooks let a project run its own steps after a sync, like reindexing Elasticsearch or removing personal data. There’s a toggle to skip them when you only need the data.

Persistent history

Before, an import showed up while it was running. Once it finished, successful or not, it was gone.

I added Previous Imports, so every import stays there now. You can see when it ran, who started it, and its status. If it failed, you can open it and see which step failed and why. The mockup had its own progress view. In the end I reused the log view from deployments so it would feel familiar.

altis-cli sync-local

This one came from an idea at the team retreat. A developer should be able to bring an Altis environment down to their local server with one command.

I built altis-cli app sync-local. It starts a backup, downloads it, and imports the database and uploads. It uses the same search-replace pipeline as the dashboard, then runs the post-sync hooks. You can sync a single site with --site-id.

Under the hood

Our CTO switched the database import to mydumper and myloader. It’s a lot faster. The dump runs as multiple streams and loads using multiple threads.

The part I liked most is that search-replace now happens in the pipeline. We forked WordPress VIP’s go-search-replace-mydumper to support mydumper’s stream mode. It rewrites the data as mydumper streams it, using several workers at once, then hands it to myloader. Search-replace no longer waits for the whole dump to finish.

Before, the sync used mysqldump. You’d wait for the import to finish, open a sandbox, then run search-replace on the whole database yourself.

Wrapping up

It shipped in Altis v27 in June. It was nice to follow one feature from a mockup to implementation to documentation. Starting with mockups made the rest easier to talk about.